﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;

namespace RWLib
{
    public class Action
    {
        public String Name;

        /// <summary>
        /// Czas wywołania akcji
        /// </summar>
        public int StartTime;

        #region Trigger
        /// <summary>
        /// (Trigger state)
        /// Fluent o określonej wartości, który w jakimkolwiek czasie powoduje samoistne wywołanie akcji
        /// </summary>
        public Fluent TriggeredBy;
        #endregion

        #region Action effect
        /// <summary>
        /// Akcja wywołana po czasie InvokeActionAfter jeśli prawdziwa jest formuła InvokeCondition
        /// </summary>
        public Action InvokeAction;

        /// <summary>
        /// Czas jaki musi upłynąć od StartTime, aby wywołać akcję InvokeAction
        /// </summary>
        public int InvokeActionAfter;

        /// <summary>
        /// Formuła, która musi być spełniona, aby po czasie InvokeActionAfter od StartTime można było wywołać akcję InvokeAction
        /// </summary>
        public Formula InvokeCondition;
        #endregion

        #region Release
        /// <summary>
        /// (Release statement)
        /// Formuła logiczna, która musi być spełniona, aby fluenty w releasedFluents mogły zostać zmienione
        /// </summary>
        public Formula releasedCondition;

        /// <summary>
        /// (Release statement)
        /// Fluenty, których wartość może (ale nie musi) być zmieniona po wykonaniu akcji
        /// </summary>
        public List<Fluent> releasedFluents;
        #endregion

        #region Fluent effect
        /// <summary>
        /// (Fluent effect statement)
        /// Formuła logiczna, która musi być spełniona, aby akcja dała efekty effects 
        /// </summary>
        public Formula condition;
        /// <summary>
        /// (Fluent effect statement)
        /// Efekty (wartości fluentów) po wykonaniu akcji 
        /// </summary>
        public List<Fluent> effects;
        #endregion

        #region Possibilities
        /// <summary>
        /// (Possibility statement)
        /// Akcja niemożliwa do wykonania jeśli przed jej wykonaniem mamy takie wartości fluentów 
        /// </summary>
        public List<Fluent> ImpossibleConditions;

        /// <summary>
        /// (Possibility statement with time restriction)
        /// Akcja niemożliwa do wykonania w danych momentach 
        /// </summary>
        public List<int> ImpossibleTimes;
        #endregion

        public Action()
        {
        }

        public Action(String name, Formula condition, List<Fluent> effects)
        {
            Name = name;
            this.condition = condition;
            this.effects = effects;
        }

        public bool IsConflictingWith(Action a2)
        {
            if (Equals(a2)) return true;
            var a2reqs = a2.condition;
            var a2effects = a2.effects;

            if (condition == null || condition.Count == 0 || a2reqs == null || a2reqs.Count == 0 &&
                effects == null || effects.Count == 0 || a2effects == null || a2effects.Count == 0) return false;

            if (condition.Conflicts(a2.condition))
            {
                return true;
            }
            foreach (var effect in effects)
            {
                if (!a2effects.Contains(effect.Name)) continue;
                if (a2effects.Get(effect).Conflicts(effects.Get(effect)))
                {
                    return true;
                }
            }
            return false;
        }

        public void Affect(List<Fluent> fluents)
        {

        }

        public bool IsExecutable(List<Fluent> fluents)
        {
            if (condition == null || condition.Count == 0) return true;
            return !condition.Conflicts(fluents);
        }

        public void Prepare(List<Fluent> fluents)
        {
            if (condition == null || condition.Count == 0) return;
            condition.Apply(fluents);
        }

        public override string ToString()
        {
            string str = Name;
            if (TriggeredBy != null)
                str += "\nTriggered by " + TriggeredBy.Name + " " + TriggeredBy.Value;

            if (effects != null)
            {
                str += "\nCauses ";
                foreach (var f in effects)
                    str += f.Name + " " + f.Value;
            }
            if (condition != null)
            {
                str += " if ";
                foreach (var f in condition.ConditionsAND)
                    str += f.Name + " " + f.Value + ", ";
            }
            if (releasedFluents != null)
            {
                str += "\nReleases: " + releasedFluents[0].Name;
                str += " if ";
                foreach (var f in releasedCondition.ConditionsAND)
                    str += f.Name + " " + f.Value + ", ";
            }

            if (ImpossibleTimes != null)
                str += "\nImpossible at time : " + ImpossibleTimes[0];
            if (ImpossibleConditions != null)
                foreach (var c in ImpossibleConditions)
                    str += "\nImpossible if " + c.Name + " " + c.Value;
            if (InvokeAction != null)
                str += "\nInvokes: " + InvokeAction.Name + " after " + InvokeActionAfter;
            if (InvokeCondition != null)
            {
                str += " if ";
                foreach (var f in InvokeCondition.ConditionsAND)
                    str += f.Name + " " + f.Value + ", ";
            }
            str += "\n---------------------";
            return str;
        }
    }
}
